/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       run_combat.h

	$Header: /game/run_combat.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "run_combat.h"

#include "adventure_ai.h"
#include "adventure_frame.h"
#include "adventure_map.h"
#include "army.h"
#include "basic_dialog.h"
#include "combat_context.h"
#include "combat_window.h"
#include "creature_array.h"
#include "dialog_score.h"
#include "game_application.h"
#include "options.h"
#include "player.h"
#include "script_context_args.h"
#include "script_targeting.h"
#include "simulated_combat.h"
#include "report_combat_results.h"


// Static storage so we can find the "waiting" dialog box when its time to close it.
t_counted_ptr<t_basic_dialog>	g_waiting_dialog_ptr;
t_creature_array*				g_waiting_attacker_ptr;
t_creature_array*				g_waiting_defender_ptr;


// Forward declaration for computing battle points in dialog_score.cpp


void run_result_scripts( t_combat_context& combat_context, t_creature_array const* losses, t_combat_result result )
{
	t_creature_array&		attacker = *combat_context.get_attacker();
	t_player*				attacking_player = attacker.get_owner();
	t_creature_array&		defender = *combat_context.get_defender();
	t_player*				defending_player = defender.get_owner();
	t_adventure_map&		adventure_map = *combat_context.get_adventure_map();
	t_adv_map_point const&	location = combat_context.get_location();
	t_adventure_frame*		adventure_frame = combat_context.get_adventure_frame();

	t_script_context_hero context_attacker( &adventure_map, NULL, &attacker, attacking_player,
											&defender, defending_player, &location);
	t_script_context_hero context_defender( &adventure_map, NULL, &defender, defending_player, 
											&attacker, attacking_player, &location);

	t_map_size map_size;

	map_size = adventure_map.get_map_size_type();
	if ( attacking_player && attacker_won( result ) )
	{
		int points;

		points = calculate_battle_victory_points( attacker, defender, losses[false], losses[true],
												  map_size );
		attacking_player->inc_battle_score( points );
	}
	else
	{
		if ( defending_player && defender_won( result ) )
		{
			int points = calculate_battle_victory_points( defender, attacker, losses[true], 
													      losses[false], map_size );
			defending_player->inc_battle_score( points );
		}
	}

	// Apply k_hero_scriptable_lose_combat to all dying heroes
	execute_hero_script(k_hero_scriptable_lose_combat, context_attacker, losses );
	execute_hero_script(k_hero_scriptable_lose_combat, context_defender, losses );

	if ( attacker_won( result ) )
	{
		execute_hero_script(k_hero_scriptable_win_combat, context_attacker, losses );
	}
	else if ( defender_won( result ) ) 
	{
		execute_hero_script(k_hero_scriptable_win_combat, context_defender, losses );
	}

}

void on_combat_end( t_combat_context&	combat_context, t_combat_result result,
			    	t_army_ptr			new_army_ptr )
{
	t_creature_array&	attacker = *combat_context.get_attacker();
	t_creature_array&	defender = *combat_context.get_defender();
	t_adventure_map&	adventure_map = *combat_context.get_adventure_map();
	t_adventure_frame*	adventure_frame = combat_context.get_adventure_frame();

	if (adventure_map.is_game_over())
		return;

	attacker.set_in_combat( false );
	defender.set_in_combat( false );

	// check the win/loss condition
	if (!adventure_map.is_game_over())
		adventure_frame->evaluate_victory_condition();
	if (!adventure_map.is_game_over())
		combat_context.on_combat_end( result );

	// Restore the cursor state (because combat_window enables it, but the AI may
	// be assuming its off)
	if (adventure_map.is_game_over())
		return;

	t_game_application::set_cursor_visible( combat_context.was_cursor_visible() );

	t_army_ptr attacker_army = attacker.get_army();
	t_army_ptr defender_army = defender.get_army();

	if (!adventure_map.is_game_over())
	{
		if ( attacker_army )
			adventure_frame->update_army(attacker_army);

		if ( defender_army )
			adventure_frame->update_army(defender_army);

		if ( new_army_ptr )
			adventure_frame->update_army(new_army_ptr);

		adventure_frame->update_armies();

		if ( new_army_ptr && new_army_ptr->get_owner_number() 
			 == adventure_map.get_player_number() )
			adventure_frame->select_army( new_army_ptr );
	}

	// Trigger any events that may have been caused by the armies
	// retreating, surrendering, or what have you
	if ( !adventure_map.is_game_over() && attacker_army != NULL )
	{
		attacker_army->trigger_events();
	}

	if ( !adventure_map.is_game_over() && new_army_ptr != NULL )
	{
		new_army_ptr->trigger_events();
	}

	if ( !adventure_map.is_game_over() && defender_army != NULL )
	{
		defender_army->trigger_events();
	}
}

// This is the main entry point for combat.  All combat starts here.
void run_combat( t_combat_context& combat_context, bool defender_ran, bool quick_combat )
{
	t_creature_array&					attacker = *combat_context.get_attacker();
	t_player*							attacking_player = attacker.get_owner();
	t_creature_array&					defender = *combat_context.get_defender();
	t_player*							defending_player = defender.get_owner();
	t_adventure_map&					adventure_map = *combat_context.get_adventure_map();
	t_adv_map_point const&				location = combat_context.get_location();
	t_town*								town = combat_context.get_town();
	t_ownable_garrisonable_adv_object const*	garrison = combat_context.get_garrison();
	t_adventure_frame*							adventure_frame = combat_context.get_adventure_frame();
	t_window_ptr						ref = adventure_frame;
	t_army_ptr							new_army_ptr; // Any new army created as a result of combat (during retreat / surrender) 

	t_script_context_hero context_attacker( &adventure_map, NULL, &attacker, attacking_player,
											&defender, defending_player, &location);
	t_script_context_hero context_defender( &adventure_map, NULL, &defender, defending_player, 
											&attacker, attacking_player, &location);
	t_combat_result result;

	execute_hero_script(k_hero_scriptable_begin_combat, context_attacker, NULL );
	execute_hero_script(k_hero_scriptable_begin_combat, context_defender, NULL );

	bool attacker_is_human = attacking_player && !attacking_player->is_computer() && !attacking_player->is_eliminated();
	bool defender_is_human = defending_player && !defending_player->is_computer() && !defending_player->is_eliminated();

	combat_context.set_cursor_visible( t_game_application::is_cursor_visible() );
	attacker.set_in_combat( true );
	defender.set_in_combat( true );
	if (defender.empty() || defender_ran)
	{
		result = k_result_defender_lost;

		t_creature_array losses[2]; // Leave losses empty; don't give out XP

		run_result_scripts( combat_context, losses, result );

		t_creature_array*	armies[2];
		t_player*			players[2];
		t_artifact_list		booty;

		armies[0] = &attacker;
		armies[1] = &defender;
		players[0] = attacking_player;
		players[1] = defending_player;

		strip_artifacts( defender, booty );
		defender.clear();

		post_process_apply_combat_results( armies, players, combat_context.get_losses(), result,
											combat_context.get_location(), adventure_frame, 
											combat_context.get_town() );

		post_process_apply_combat_surrender( armies, players, combat_context.get_losses(), result,
										   combat_context.get_location(), adventure_frame, 
										   combat_context.get_town(), booty,
										   combat_context.get_are_real_armies(),
										   new_army_ptr );
	}
	else if (quick_combat || use_quick_combat() || (!attacker_is_human && !defender_is_human) )
	{
		t_simulated_combat simulation( attacker, attacking_player, defender,
									   defending_player, adventure_map, location, town );

		if (!attacker_is_human && !defender_is_human 
				&& 
					( ((attacking_player == NULL) && ( defending_player != NULL))
						||
						((attacking_player != NULL) && (defending_player == NULL)) ) )
		{
			t_difficulty difficulty = adventure_map.get_player_difficulty(); 
			float effectiveness = 0.0f;
			switch (difficulty)
			{
				case k_difficulty_easy:
					effectiveness = 1.0f;
					break;
				case k_difficulty_normal:
					effectiveness = 1.30f;
					break;
				case k_difficulty_hard:
					effectiveness = 1.37f;
					break;
				case k_difficulty_expert:
					effectiveness = 1.44f;
					break;
				case k_difficulty_impossible:
					effectiveness = 1.50f;
					break;
			}
			if (attacking_player == NULL)
			{
				simulation.set_effectiveness( false, 1.0f );
				simulation.set_effectiveness( true, effectiveness );
			}
			else
			{
				simulation.set_effectiveness( true, 1.0f );
				simulation.set_effectiveness( false, effectiveness );
			}
		}

		simulation.run();
		simulation.record_results( adventure_frame );

		if (attacker_is_human || defender_is_human)
			simulation.report_results( adventure_frame );

		result = simulation.get_result();

		t_creature_array* armies[2] = { &attacker, &defender };
		t_player* players[2] = { attacking_player, defending_player };

		post_process_apply_combat_results( armies, players, simulation.get_losses(), 
											result, combat_context.get_location(), 
											adventure_frame, combat_context.get_town() );

		run_result_scripts( combat_context, simulation.get_losses(), result );

		if (!adventure_map.is_game_over())
			simulation.finalize_combat( adventure_frame, combat_context.get_are_real_armies(), new_army_ptr );
	}
	else 
	{
		// Since we're forced into using a global for storage by the fact that run_combat isn't
		// a member function (of adventure_frame for instance), make sure we don't collide.
		assert( g_waiting_dialog_ptr == NULL );
		
		if (!attacker_is_human && defender_is_human && (&adventure_map.get_player() != defending_player))
			request_player( adventure_map, defending_player );
		
		// Get the local human started
		t_counted_ptr<t_combat_window> combat_window;
		
		combat_window = new t_combat_window( &combat_context, 0 );
		combat_window->start();
		combat_window->run_modal();
		result = combat_window->get_result();
		new_army_ptr = combat_window->get_new_army_ptr();
		// note: combat window runs result scripts, so that's not needed here.
		
	}
	on_combat_end( combat_context, result, new_army_ptr );
}